#pragma once

#include <unordered_map>
#include <vector>
#include <string>
#include <assert.h>
#include <algorithm>
#include <mysql/mysql.h>

#include "../comm/utils.hpp"
#include "../comm/log.hpp"

namespace ns_model_mysql
{
    using namespace ns_log;
    using namespace ns_utils;
    using std::unordered_map;
    using std::vector;
    using std::string;

    struct Question
    {
        string number; // 题目编号
        string title; // 题目标题
        string star; // 题目难度等级，简单、中等、困难
        int cpu_limit; // cpu时间限制
        int mem_limit; // 内存空间限制
        string  desc; // 题目描述
        string basic_code; // 基本代码,预设在代码编辑器中的代码
        string test_code; // 测试代码
    };

    const string host = "127.0.0.1";
    const int port = 3306;
    const string user = "oj_admin";
    const string passwd = "Huawei12#$";
    const string database = "oj";
    const string table_name = "question_info";

    class Model
    {
    private:
        /**
         * @brief 执行sql查询语句，并返回查询结果
         *
         * @param[in] sql sql语句
         *
         * @param[in] questions 查询结果
         *
         * @return true 或 false
         */
        bool query_mysql(const string& sql,vector<Question>* questions)
        {
            /* 1. 创建mysql句柄 */
            MYSQL* handle = mysql_init(nullptr);

            /* 2. 连接数据库 */
            if(nullptr == mysql_real_connect(handle,host.c_str(),user.c_str(),passwd.c_str(),database.c_str(),port, nullptr,0))
            {
                LOG(FATAL) << "连接数据库失败" << std::endl;

                return false;
            }

            /* 3. 设置连接编码格式为"utf8"，默认为"latin1"，会存在中文乱码 */
            mysql_set_character_set(handle,"utf8");

            /* 4. 执行sql语句,执行成功返回0 */
            if(mysql_query(handle,sql.c_str()))
            {
                LOG(WARING) << sql << " execute error!" << std::endl;

                return false;
            }

            /* 5. 解析结果 */
            MYSQL_RES* res = mysql_store_result(handle);
            int rows = mysql_num_rows(res); // 行数
            int cols = mysql_num_fields(res); // 列数
            Question q;
            for(int i = 0;i < rows;i++)
            {
                MYSQL_ROW content = mysql_fetch_row(res);
                q.number = content[0];
                q.title = content[1];
                q.star = content[2];
                q.desc = content[3];
                q.cpu_limit = std::stoi(content[4]);
                q.mem_limit = std::stoi(content[5]);
                q.basic_code = content[6];
                q.test_code = content[7];

                questions->push_back(q);
            }

            /* 6. 释放空间，关闭连接*/
            mysql_free_result(res);
            mysql_close(handle);

            return true;
        }
    public:
        /**
         * @brief 获取所有题目信息到容器中
         *
         * @param[out] questions 存放题目信息的容器
         *
         * @return true 或 false
         */
        bool get_all_questions(vector<Question>* questions)
        {
            bool res = false;
            string sql = "select * from ";
            sql += table_name;
            if(query_mysql(sql,questions))
            {
                sort(questions->begin(), questions->end(), [](const struct Question &q1, const struct Question &q2){
                    return stoi(q1.number) < stoi(q2.number);
                });
                res = true;
            }

            return res;
        }

        /**
         * @brief 获取题目编号对应题目信息
         *
         * @param[in] 题目编号
         *
         * @param[out] question 存放题目信息
         *
         * @return true 或 false
         */
        bool get_one_question(const string& number,Question* question)
        {
            bool res = false;
            string sql = "select * from ";
            sql += table_name;
            sql += " where number=";
            sql += number;
            vector<Question> tmp;
            if(query_mysql(sql,&tmp))
            {
                if(1 == tmp.size())
                {
                    *question = tmp[0];
                    res = true;
                }
            }

            return true;
        }
    };
}
